import { Outlet, Link, useLoaderData, Form, redirect, NavLink, useNavigation, useSubmit} from "react-router-dom";
import { getContacts, createContact } from "../contacts";
import { useEffect } from 'react'


export async function loader({ request }) {
  console.log('root loader doing!')
  const url = new URL(request.url);
  const q = url.searchParams.get("q");
  const contacts = await getContacts(q);
  return { contacts, q };
}

export async function action() {
  const contact = await createContact();
  return redirect(`/contacts/${contact.id}/edit`);
}

export default function Root() {
  // 会阻塞组件渲染，会等到数据获取到才开始渲染组件
  // 实践证明：切换路由的时候（比如点击link）Root会重新渲染，但是loader不会重新执行，会复用前一次渲染中获得的loader数据
  const { contacts, q } = useLoaderData();
  const navigation = useNavigation();
  const submit = useSubmit(); // 类似于useNavigate

  // 这个地方老是输出两遍的原因（即这个组件render两遍的原因）：
  //   因为组件中的loader执行或者action执行或者link点击，都是异步的，意味着不能马上得到结果
  //   因此这里重新render两遍，第一遍是因为异步在进行中，在这里我们可以展示一些loading过渡效果，第二遍是异步完成后，用最终数据渲染
  //   更多的相当于react中的useTransition或者usedefferValue，会降低当前任务优先级（因为其中可能涉及到一些异步获取）
  //   所以这也是为什么NavLink切换路由的时候，可以使用useNavigation拿到过渡状态
  console.log('Root render!' + `q=${q}`)

  // navigation可以记录当前render是过渡阶段(navigation.state == 'loading')渲染还是完成时(idle)渲染
  // 只有在过渡阶段才会存在navigation.location（描述即将切换到的路由的路径对象）
  // 官方：当应用导航到新 URL 并为其加载数据时，将显示 navigation.location 。当不再有待处理的导航时，它就会消失。
  const searching =
    navigation.location &&
    new URLSearchParams(navigation.location.search).has(
      "q"
    );
  // console.log(navigation.location)
  // console.log(navigation.state)

  // 实践证明：
  // 1. 所有路由路径的改变由react客户端路由接管
  // 2. 通过浏览器左上方按钮前进后退页面，也不会发生页面请求，而且所有当前路径涉及的loader都会重新执行
  // 3. loader重新执行意味着对应的组件页面会render两次，一次是过渡中（状态还是之前的状态），一次是完成时
  useEffect(() => {
    document.getElementById("q").value = q;
  }, [q]);
  return (
    <>
      <div id="sidebar">
        <h1>React Router Contacts</h1>
        <div>
          {/* 
            TODO:
             注意这个表单的method是GET，而action都是处理POST表单的
             所以这里我们使用loader来处理get表单的提交，确实是有道理的
             loader本身处理的就是当前路由组件被路由选中时执行预加载数据，然后
             现在路由路径其实没变，只是多了一些搜索参数，而这些搜索参数可能决定了当前加载数据的不同
             所以通过loader来重新拉取
          */}
          <Form id="search-form" role="search">
            <input
              id="q"
              className={searching ? "loading" : ""}
              aria-label="Search contacts"
              placeholder="Search"
              type="search"
              name="q"
              defaultValue={q}
              // onChange相当于vue中的@input
              // 实践证明：
              //   每输入一个字母-> 每次都会马上提交-> loader调用-> 过渡阶段render执行-> 
              //   （接上）如果loader中涉及到耗时很长的任务（猛猛调用-> 只有最后一个耗时任务决定组件的完成时render执行（但是网络请求应该不会自动cancel）
              onChange={(event) => {
                const isFirstSearch = q == null;
                // submit 函数将序列化并提交您传递给它的任何表单
                // get表单提交 -> 被react拦截 -> 改变url查询参数 -> 触发表单所在组件的重新loader -> 该组件render两次
                // 实践证明：submit和navigate都可以切换路由，切换路由被react前端路由拦截（loader看情况可能执行可能不执行）
                submit(event.currentTarget.form, {
                  replace: !isFirstSearch,
                });
              }}
            />
            <div
              id="search-spinner"
              aria-hidden
              hidden={!searching}
            />
            <div
              className="sr-only"
              aria-live="polite"
            ></div>
          </Form>
          <Form method="post">
            <button type="submit">New</button>
          </Form>
        </div>
        <nav>
          {contacts.length ? (
            <ul>
              {contacts.map((contact) => (
                <li key={contact.id}>
                  <NavLink
                    to={`contacts/${contact.id}`}
                    className={({ isActive, isPending }) =>
                      isActive
                        ? "active"
                        : isPending
                        ? "pending"
                        : ""
                    }
                  >
                    {contact.first || contact.last ? (
                      <>
                        {contact.first} {contact.last}
                      </>
                    ) : (
                      <i>No Name</i>
                    )}{" "}
                    {contact.favorite && <span>★</span>}
                  </NavLink>
                </li>
              ))}
            </ul>
          ) : (
            <p>
              <i>No contacts</i>
            </p>
          )}
        </nav>
      </div>
      <div id="detail" 
        className={
          navigation.state === "loading" ? "loading" : ""
        }
      >
        <Outlet></Outlet>
      </div>
    </>
  );
}